package cn.slipi.admin.common.handler.exception;

import cn.slipi.admin.common.exception.base.BizException;
import cn.slipi.admin.common.exception.base.ErrorMessage;
import cn.slipi.admin.common.response.builder.ResponseBuilder;
import cn.slipi.admin.common.utils.ExceptionUtil;
import okio.Okio;
import org.apache.commons.codec.Charsets;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.io.IOException;
import java.util.Set;

/**
 * 默认的处理异常类
 *
 * @author lee
 */
@Component
public final class DefaultExceptionHandler implements HandlerExceptionResolver {

    private static final Logger LOG = LoggerFactory.getLogger("exLogger");

    @Autowired
    private MessageSource messageSource;

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        if (ex instanceof MethodArgumentNotValidException) {
            response.setStatus(400);
            MethodArgumentNotValidException mane = (MethodArgumentNotValidException) ex;
            BindingResult bindingResult = mane.getBindingResult();
            FieldError fieldError = bindingResult.getFieldError();
            String errMsg = fieldError == null ? ex.getMessage() : fieldError.getDefaultMessage();
            String field = fieldError == null ? "" : fieldError.getField();
            return ResponseBuilder.build(new ErrorMessage(String.format("[%s]:%s", field, errMsg)));
        } else if (ex instanceof ConstraintViolationException) {
            response.setStatus(400);
            ConstraintViolationException vex = (ConstraintViolationException) ex;
            Set<ConstraintViolation<?>> constraintViolations = vex.getConstraintViolations();
            String msg = "";
            for (ConstraintViolation violation : constraintViolations) {
                msg = violation.getMessage();
                if (StringUtils.isNotBlank(msg)) {
                    break;
                }
            }
            return ResponseBuilder.build(new ErrorMessage(String.format("%s", msg)));
        } else if (ex instanceof MissingServletRequestParameterException) {
            response.setStatus(400);
            MissingServletRequestParameterException missingServletRequestParameterException = (MissingServletRequestParameterException) ex;
            return ResponseBuilder.build(new ErrorMessage(String.format("[%s]字段不能为空", missingServletRequestParameterException.getParameterName())));
        } else if (ex instanceof NoHandlerFoundException) {
            response.setStatus(404);
            return ResponseBuilder.build(new ErrorMessage(404, "404 NotFound"));
        } else if (ex instanceof BizException) {
            BizException bizException = (BizException) ex;
            response.setStatus(200);
            try {
                bizException.setMsg(messageSource.getMessage(((BizException) ex).getMsg(), new Object[]{}, LocaleContextHolder.getLocale()));
            } catch (Exception e) {
                LOG.error(e.getMessage());
            }
            return ResponseBuilder.build(new ErrorMessage(bizException));
        } else {
            response.setStatus(500);
            LOG.error(request.getMethod() + request.getServletPath());
            LOG.error(request.getQueryString());
            try {
                LOG.error(Okio.buffer(Okio.source(request.getInputStream())).readString(Charsets.UTF_8));
            } catch (IOException ioe) {
                //ignore
            }
            LOG.error(ExceptionUtil.getStackTraceAsString(ex));
            return ResponseBuilder.build(new ErrorMessage("内部错误"));
        }
    }
}
